From 07648a787d862e40fef74a22055a8b638669b963 Mon Sep 17 00:00:00 2001 From: robertl Date: Wed, 22 Jan 2003 03:35:29 +0000 Subject: [PATCH] Add GeocachingDB input and output module. --- gpsbabel/Makefile | 2 +- gpsbabel/README | 12 + gpsbabel/gcdb.c | 343 ++++++++++++++++++++++++++++ gpsbabel/reference/GeocachingDB.PDB | Bin 0 -> 5822 bytes gpsbabel/testo | 12 + gpsbabel/vecs.c | 7 + 6 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 gpsbabel/gcdb.c create mode 100644 gpsbabel/reference/GeocachingDB.PDB diff --git a/gpsbabel/Makefile b/gpsbabel/Makefile index ff7fb391a..62e750198 100644 --- a/gpsbabel/Makefile +++ b/gpsbabel/Makefile @@ -3,7 +3,7 @@ CFLAGS=-g -Icoldsync FMTS=magproto.o gpx.o geo.o gpsman.o mapsend.o mapsource.o \ gpsutil.o tiger.o pcx.o csv.o cetus.o gpspilot.o magnav.o \ psp.o mxf.o holux.o garmin.o ozi.o tmpro.o dna.o tpg.o gpsdrive.o \ - xcsv.o xmapwpt.o + xcsv.o xmapwpt.o gcdb.o JEEPS=jeeps/gpsapp.o jeeps/gpscom.o jeeps/gpsfmt.o jeeps/gpsinput.o \ jeeps/gpsmath.o jeeps/gpsmem.o \ diff --git a/gpsbabel/README b/gpsbabel/README index 0979321b3..578eaaa94 100644 --- a/gpsbabel/README +++ b/gpsbabel/README @@ -279,6 +279,18 @@ THE FORMATS against GpsDrive v 1.30 found @ http://www.kraftvoll.at/software. Contributed by Alan Curry. + Geocaching DB + + This is a PDA file format. It was tested against version 2 + of GeocachingDB and a development snapshot of version 3. + Information on the file format came from Dougs Brat and Ron Parker. + A particularly handy way to use GPSBabel on these files is to use + GPSBabel to read a GPX file with Groundspeak (geocaching.com) + extensions and let it write you a GeocachingDB file that contains + the cache names, difficulty, terrain, and such. + + http://vip.hyperusa.com/~dougs/geocachingdb/geocachingdb.htm + COMMON USAGE diff --git a/gpsbabel/gcdb.c b/gpsbabel/gcdb.c new file mode 100644 index 000000000..2021b665a --- /dev/null +++ b/gpsbabel/gcdb.c @@ -0,0 +1,343 @@ +/* + Read and write GeocachingPDB files. + + Copyright (C) 2002 Robert Lipe, robertlipe@usa.net + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA + + */ + +#include "defs.h" +#include "coldsync/palm.h" +#include "coldsync/pdb.h" + +#define MYNAME "GeocachingDB" +#define MYTYPE 0x44415441 /* DATA */ +#define MYCREATOR 0x42726174 /* Brat */ + +#define MAXRECSZ 500 /* This is overkill as the records seem to be around 100 + bytes a piece, but being conservative and dealing + with realloc issues just doesn't seem worth it. */ + +typedef enum { + RECTYPE_TEXT = 0, + RECTYPE_DATE = 2 +} gcdb_rectype; + +struct dbfld { + char fldname[4]; + pdb_16 fldtype; + pdb_16 fldlen; +}; + +struct dbrec { + pdb_16 nflds; + struct dbfld dbfld[1]; +}; + +static FILE *file_in; +static FILE *file_out; +static const char *out_fname; +struct pdb *opdb; +struct pdb_record *opdb_rec; + + +static void +rd_init(const char *fname, const char *args) +{ + file_in = fopen(fname, "rb"); + if (file_in == NULL) { + fatal(MYNAME ": Cannot open %s for reading\n", fname); + } +} + +static void +rd_deinit(void) +{ + fclose(file_in); +} + +static void +wr_init(const char *fname, const char *args) +{ + file_out = fopen(fname, "wb"); + out_fname = fname; + if (file_out == NULL) { + fatal(MYNAME ": Cannot open %s for writing\n", fname); + } +} + +static void +wr_deinit(void) +{ + fclose(file_out); +} + +static void +data_read(void) +{ + struct dbrec *rec; + struct pdb *pdb; + struct pdb_record *pdb_rec; + + if (NULL == (pdb = pdb_Read(fileno(file_in)))) { + fatal(MYNAME ": pdb_Read failed\n"); + } + + if ((pdb->creator != MYCREATOR) || (pdb->type != MYTYPE)) { + fatal(MYNAME ": Not a GeocachingDB file.\n"); + } + + for(pdb_rec = pdb->rec_index.rec; pdb_rec; pdb_rec=pdb_rec->next) { + waypoint *wpt = xcalloc(sizeof(*wpt),1); + struct dbrec *rec = (struct dbrec *) pdb_rec->data; + int nflds; + int length; + int type; + int i; + char *recdata; + int lat_dir = 0; + int lat_deg = 0; + float lat_min = 0.0; + int lon_dir = 0; + int lon_deg = 0; + float lon_min = 0.0; + + nflds = be_read16(&rec->nflds); + recdata = (char *) &rec->dbfld[nflds]; + + for (i = 0; i < nflds; i++) { + length = (unsigned short) be_read16(&rec->dbfld[i].fldlen); + type = be_read16(&rec->dbfld[i].fldtype); + + switch(type) { + case RECTYPE_TEXT: /* Text */ + if (!strncmp("gcid", rec->dbfld[i].fldname,4)) { + wpt->shortname = xstrdup(recdata); + } else + if (!strncmp("gcna", rec->dbfld[i].fldname,4)) { + wpt->description = xstrdup(recdata); + } else + if (!strncmp("lat0", rec->dbfld[i].fldname,4)) { + lat_dir = *recdata == 'N' ? 1 : -1; + } else + if (!strncmp("lat1", rec->dbfld[i].fldname,4)) { + lat_deg = atoi(recdata); + } else + if (!strncmp("lat2", rec->dbfld[i].fldname,4)) { + lat_min = atof(recdata); + } + if (!strncmp("lon0", rec->dbfld[i].fldname,4)) { + lon_dir = *recdata == 'E' ? 1 : -1; + } else + if (!strncmp("lon1", rec->dbfld[i].fldname,4)) { + lon_deg = atoi(recdata); + } else + if (!strncmp("lon2", rec->dbfld[i].fldname,4)) { + lon_min = atof(recdata); + } else + if (!strncmp("take", rec->dbfld[i].fldname,4)) { + wpt->notes = xstrappend(wpt->notes, " Took "); + wpt->notes = xstrappend(wpt->notes, recdata); + } else + if (!strncmp("left", rec->dbfld[i].fldname,4)) { + wpt->notes = xstrappend(wpt->notes, " Left "); + wpt->notes = xstrappend(wpt->notes, recdata); + } else + if (!strncmp("diff", rec->dbfld[i].fldname,4)) { + wpt->gc_data.diff = 10 * atof(recdata); + } else + if (!strncmp("terr", rec->dbfld[i].fldname,4)) { + wpt->gc_data.terr = 10 * atof(recdata); + } + break; +#if 0 + /* This really is the date of the find, + * not the cache creation date. + */ + case RECTYPE_DATE: + if (!strncmp("date", rec->dbfld[i].fldname,4)) { + time_t tm; + tm = be_read32(recdata) * 24 * 3600; + tm -= EPOCH_1904; + wpt->creation_time = tm; + fprintf(stderr, "date %d\n", tm); + } + break; +#endif + } + recdata += (length + 1) & (~1); + } + wpt->position.latitude.degrees = lat_dir * (lat_deg + lat_min/60); + wpt->position.longitude.degrees = lon_dir * (lon_deg + lon_min/60); + waypt_add(wpt); + } + + free_pdb(pdb); +} + +static int +gcdb_add_to_rec(struct dbrec *rec, char *fldname, gcdb_rectype rectype, void *data) +{ + int length; + static char *tbuf; + static char *tbufp; + static int rec_cnt; + + if (fldname == NULL) { + length = tbufp - tbuf; + be_write16(&rec->nflds, rec_cnt); + memcpy(&rec->dbfld[rec_cnt],tbuf, length); + tbufp = tbuf; + length += 4 + sizeof(struct dbfld) * rec_cnt; + return length; + } + + if (!tbuf) { + tbuf = xcalloc(MAXRECSZ, 1); + tbufp = tbuf; + } + + be_write16(&rec->dbfld[rec_cnt].fldtype,rectype); + strncpy(rec->dbfld[rec_cnt].fldname, fldname, 4); + + switch (rectype) { + case RECTYPE_TEXT: + length = 1 + strlen(data); + be_write16(&rec->dbfld[rec_cnt].fldlen, length); + strcpy(tbufp, data); + tbufp += (length + 1) & (~1); + break; + case RECTYPE_DATE: + length = 4; + be_write16(&rec->dbfld[rec_cnt].fldlen, length); + be_write32(tbufp, ((time_t)data - EPOCH_1904)/ (3600 * 24)); + tbufp += length; + break; + default: + abort(); + } + rec_cnt++; +} + +static void +gcdb_write_wpt(const waypoint *wpt) +{ + struct dbrec *rec; + char *vdata; + char *recdata; + static int ct; + int length; + int reclen; + char tbuf[100]; + + /* + * We don't really know how many fields we'll have or how long + * they'll be so we'll just lazily create a huge place to hold them. + */ + rec = xcalloc(sizeof(*rec) + 5000, 1); + + gcdb_add_to_rec(rec, "gcna", RECTYPE_TEXT, wpt->description); + gcdb_add_to_rec(rec, "gcid", RECTYPE_TEXT, wpt->shortname); + + gcdb_add_to_rec(rec, "lat0", RECTYPE_TEXT, + wpt->position.latitude.degrees < 0 ? "S" : "N"); + + sprintf(tbuf, "%d", (int) wpt->position.latitude.degrees); + gcdb_add_to_rec(rec, "lat1", RECTYPE_TEXT, tbuf); + + sprintf(tbuf, "%f", 60 * (wpt->position.latitude.degrees - + (int) wpt->position.latitude.degrees)); + gcdb_add_to_rec(rec, "lat2", RECTYPE_TEXT, tbuf); + + + gcdb_add_to_rec(rec, "lon0", RECTYPE_TEXT, + wpt->position.longitude.degrees < 0 ? "W" : "E"); + + sprintf(tbuf, "%d", (int) wpt->position.longitude.degrees); + gcdb_add_to_rec(rec, "lon1", RECTYPE_TEXT, tbuf); + + sprintf(tbuf, "%f", 60 * (wpt->position.longitude.degrees - + (int) wpt->position.longitude.degrees)); + gcdb_add_to_rec(rec, "lon2", RECTYPE_TEXT, tbuf); + + if (wpt->gc_data.diff) { + sprintf(tbuf, "%f", wpt->gc_data.diff / 10.0); + gcdb_add_to_rec(rec, "diff", RECTYPE_TEXT, tbuf); + } + + if (wpt->gc_data.terr) { + sprintf(tbuf, "%f", wpt->gc_data.terr / 10.0); + gcdb_add_to_rec(rec, "terr", RECTYPE_TEXT, tbuf); + } + +#if 0 + /* This really is the date of the find, + * not the cache creation date. + */ + if (wpt->creation_time) { + gcdb_add_to_rec(rec, "date", RECTYPE_DATE, (void *) wpt->creation_time); + } +#endif + + /* + * We're done. Build the record. + */ + reclen = gcdb_add_to_rec(rec, NULL, 0, NULL); + + opdb_rec = new_Record(0, 2, ct++, reclen, (const ubyte *)rec); + + if (opdb_rec == NULL) { + fatal(MYNAME ": libpdb couldn't create record\n"); + } + + if (pdb_AppendRecord(opdb, opdb_rec)) { + fatal(MYNAME ": libpdb couldn't append record\n"); + } + + free(rec); +} + +static void +data_write(void) +{ + + if (NULL == (opdb = new_pdb())) { + fatal (MYNAME ": new_pdb failed\n"); + } + + strncpy(opdb->name, out_fname, PDB_DBNAMELEN); + strncpy(opdb->name, "GeocachingDB", PDB_DBNAMELEN); + opdb->name[PDB_DBNAMELEN-1] = 0; + opdb->attributes = PDB_ATTR_BACKUP; + opdb->ctime = opdb->mtime = time(NULL) + 2082844800U; + opdb->type = MYTYPE; /* CWpt */ + opdb->creator = MYCREATOR; /* cGPS */ + opdb->version = 1; + + waypt_disp_all(gcdb_write_wpt); + + pdb_Write(opdb, fileno(file_out)); +} + + +ff_vecs_t gcdb_vecs = { + rd_init, + wr_init, + rd_deinit, + wr_deinit, + data_read, + data_write, +}; diff --git a/gpsbabel/reference/GeocachingDB.PDB b/gpsbabel/reference/GeocachingDB.PDB new file mode 100644 index 0000000000000000000000000000000000000000..4f6c38b6ba2ec34822bf548f5317cfdc146d595d GIT binary patch literal 5822 zcmeHL%TC)s6urF4qm{a7)g_v4T99C{XZ(oOhvb+fg_eLqppQ*8Nemc_Eg1(IRCV1S z=)T`ksZv)}KdOt~aU2|~AhpFv70L*6CSzliGv}PSGZi;%isqgd>@F8!P@960FPmRK z$?Nyq%XZx^Mj}2NpTXYe(d6SufH(p$`VkR-#(v6Q02AL3#{njPAWi_x+(WzpaO*qb zB*1NnQvi2n5vTE0GrP?|AU@h{dM(6BU&IRHD58ouhR6_S{4nT_hk+a)$Io*7X3N{z zL7a}=D3VxRd9_}BxJl~IN@TrSx=$J%@}a)xl2y?;S|IgEw7l301K|?~nQ_USO7$hY z<^d`W<4zf%3fuzFRm!<)6%=?0i>3~X8lwg`z2 zv$@i81usjPF6PCwlw3Uuk6j{Sf;I|Tof9u`|3Z$aMoTroN;xl3OIIbusT#RPnUTm7 znNHQHupax8p}8xd>A!wu-g8)V#jzWa)HG<6noJCTfz-U_0eL`{@%F$-VHhcR&10f) zt!($^P**5tyx%vR>UuJOGndU_FI$jonQ_B*2?kP47C_!1Nh z|MUtNNOeV1UER2B-QpF4c0=l@X=BE`4c-U)VsN%BF{vlUdACks8mA^EMy4iB$JJPo zF~hU^<*sYvVtk1*HKM?Uip`zltb+uV> zjG|_zb+x8z)Jih$tdhYenJ)F(SIL~y>&A7StKoOcpMkvfUlOMsvex!vZ&B{2lQMqI zjm2q0w6G9d^$rg^$&H2dsdR72WLTsAHPyylYqI)lxQ#5Zb9v3FqA?lZS^eJELw4Te z&(QFVLk9kwfz(f>yw;PgBX_6mla<&#Jjzy+3mi4z%qH;_RzbSToV2QD=qs~pquzN9 z`btTv>OMDR6%l@WdEzu|Lk6x51F27+=H=7UyEqd6Y|zA?y}Hkl$EV#x!pxrr?t1Y^ Li=Mf%uLtl8M3dT< literal 0 HcmV?d00001 diff --git a/gpsbabel/testo b/gpsbabel/testo index 1955661be..0ca356992 100755 --- a/gpsbabel/testo +++ b/gpsbabel/testo @@ -195,3 +195,15 @@ ${PNAME} -i mapsource -f reference/mapsource.mps -o gpx -F ${TMPDIR}/ms1.gpx ${PNAME} -i mapsource -f reference/mapsource.mps -o mapsource -F ${TMPDIR}/ms.mps ${PNAME} -i mapsource -f ${TMPDIR}/ms.mps -o gpx -F ${TMPDIR}/ms2.gpx diff ${TMPDIR}/ms1.gpx ${TMPDIR}/ms2.gpx + +# +# Geocaching Database is a binary Palm format that, like the GPX variants +# has a zillion "equivalent" encodings of any given record set. So we +# read the reference file, spin it to GPX and back to GCDB and then spin +# that one to GPX. +# + +${PNAME} -i gcdb -f reference/GeocachingDB.PDB -o gpx -F ${TMPDIR}/gcdb1.gpx \ + -o gcdb -F ${TMPDIR}/gcdb1.pdb +${PNAME} -i gpx -f ${TMPDIR}/gcdb1.gpx -o gpx -F ${TMPDIR}/gcdb2.gpx +diff ${TMPDIR}/gcdb1.gpx ${TMPDIR}/gcdb1.gpx diff --git a/gpsbabel/vecs.c b/gpsbabel/vecs.c index b6e7ddc87..f273878f5 100644 --- a/gpsbabel/vecs.c +++ b/gpsbabel/vecs.c @@ -54,6 +54,7 @@ extern ff_vecs_t xmap_vecs; extern ff_vecs_t xmapwpt_vecs; extern ff_vecs_t tmpro_vecs; extern ff_vecs_t gpsdrive_vecs; +extern ff_vecs_t gcdb_vecs; static vecs_t vec_list[] = { @@ -206,6 +207,12 @@ vecs_t vec_list[] = { "GpsDrive Format", NULL }, + { + &gcdb_vecs, + "gcdb", + "Geocaching Database", + NULL + }, { NULL, -- 2.30.2